﻿//========================================================= 
/**@file HWndHelper.h
 * @brief Windows窗口句柄类操作辅助函数
 * 
 * @date 2018-08-27   09:17:01
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_HWNDHELPER_H_
#define _LIBZHOUYB_HWNDHELPER_H_
//--------------------------------------------------------- 
#include "../../../include/Base.h"

#include <tlhelp32.h>

#pragma comment(lib, "gdi32.lib")
//--------------------------------------------------------- 
namespace zhou_yb {
namespace extension {
namespace ability {
//--------------------------------------------------------- 
/**
 * @brief 将窗口前置
 * @date 2018-08-27 10:07
 */
class ForgroundHWnd
{
protected:
    HWND _hWnd;
    HWND _hInsertAfterWnd;
    HWND _hForgroundWnd;
    BOOL _isMin;
public:
    ForgroundHWnd() : _hWnd(NULL), _hInsertAfterWnd(NULL), _hForgroundWnd(NULL) {}
    ForgroundHWnd(HWND hWnd, bool isTopWindow = false)
    {
        _hInsertAfterWnd = NULL;
        _hForgroundWnd = NULL;
        Forground(hWnd, isTopWindow);
    }
    void Forground(HWND hWnd, bool isTopWindow = false)
    {
        _hWnd = hWnd;
        _hForgroundWnd = GetForegroundWindow();
        _isMin = IsIconic(hWnd);
        if(_isMin)
        {
            SendMessage(hWnd, WM_SYSCOMMAND, SC_RESTORE, NULL);
            SetWindowPos(_hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE);
            SetForegroundWindow(_hForgroundWnd);
        }
        if(isTopWindow)
        {
            _hInsertAfterWnd = GetWindow(_hWnd, GW_HWNDNEXT);
            SetWindowPos(_hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
        }
    }
    virtual ~ForgroundHWnd()
    {
        if(_hWnd != NULL)
        {
            if(_isMin)
            {
                SendMessage(_hWnd, WM_SYSCOMMAND, SC_MINIMIZE, NULL);
            }
            else
            {
                if(_hInsertAfterWnd != NULL)
                {
                    SetWindowPos(_hWnd, _hInsertAfterWnd, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
                }
            }
        }
        if(_hForgroundWnd != NULL)
        {
            SetForegroundWindow(_hForgroundWnd);
        }
    }
};
/**
 * @brief 附加输入
 * @date 2018-08-27 10:07
 */
class AttachInputHWnd
{
protected:
    DWORD _dwProcessId;
    DWORD _dwThisID;
public:
    AttachInputHWnd() : _dwProcessId(0), _dwThisID(0) {}
    AttachInputHWnd(HWND hWnd)
    {
        Attach(hWnd);
    }
    void Attach(HWND hWnd)
    {
        DWORD pid = 0;
        DWORD dwProcessId = GetWindowThreadProcessId(hWnd, &pid);
        DWORD dwThisID = GetCurrentThreadId();
        if(AttachThreadInput(dwProcessId, dwThisID, TRUE))
        {
            _dwProcessId = dwProcessId;
            _dwThisID = dwThisID;
        }
    }
    /// 是否Attach成功
    inline bool IsValid()
    {
        return _dwProcessId != 0 && _dwThisID != 0;
    }
    virtual ~AttachInputHWnd()
    {
        if(IsValid())
        {
            AttachThreadInput(_dwProcessId, _dwThisID, FALSE);
        }
    }
};
/**
 * @brief 移动窗口位置
 * @date 2018-08-27 10:14
 */
class MoveHWnd
{
protected:
    HWND _hWnd;
    int _left;
    int _top;
    uint _width;
    uint _height;
    DWORD _mode;

    /// 初始化字段为原始位置
    void _init(HWND hWnd)
    {
        RECT rect;
        GetWindowRect(hWnd, &rect);

        _hWnd = hWnd;
        _left = rect.left;
        _top = rect.top;
        _width = rect.right - rect.left;
        _height = rect.bottom - rect.top;
    }
public:
    MoveHWnd() : _hWnd(NULL) {}
    MoveHWnd(HWND hWnd, int left, int top)
    {
        Move(hWnd, left, top, 0, 0);
    }
    MoveHWnd(HWND hWnd, int left, int top, uint width, uint height)
    {
        Move(hWnd, left, top, width, height);
    }
    void Move(HWND hWnd, int left, int top, uint width, uint height)
    {
        // 不需要操作
        _init(hWnd);
        bool isNoSize = (width < 1 || height < 1);
        if(left == _left && top == _top)
        {
            if(isNoSize || (width == _width && height == _height))
            {
                _hWnd = NULL;
                return;
            }
        }
        _mode = SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOZORDER;
        if(isNoSize) _mode |= SWP_NOSIZE;
        SetWindowPos(_hWnd, NULL, left, top, width, height, _mode);
    }
    virtual ~MoveHWnd()
    {
        if(_hWnd != NULL)
        {
            SetWindowPos(_hWnd, NULL, _left, _top, _width, _height, _mode);
        }
    }
};
/**
 * @brief 设置窗口大小
 * @date 2018-08-27 13:08
 */
class SizeHWnd
{
protected:
    HWND _hWnd;
    int _left;
    int _top;
    uint _width;
    uint _height;
    DWORD _mode;

    /// 初始化字段为原始位置
    void _init(HWND hWnd)
    {
        RECT rect;
        GetWindowRect(hWnd, &rect);

        _hWnd = hWnd;
        _left = rect.left;
        _top = rect.top;
        _width = rect.right - rect.left;
        _height = rect.bottom - rect.top;
    }
public:
    SizeHWnd() : _hWnd(NULL) {}
    SizeHWnd(HWND hWnd, uint width, uint height)
    {
        Size(hWnd, width, height);
    }
    void Size(HWND hWnd, uint width, uint height)
    {
        _init(hWnd);
        _mode = SWP_NOMOVE | SWP_SHOWWINDOW | SWP_FRAMECHANGED;
        SetWindowPos(_hWnd, HWND_TOP, 0, 0, width, height, _mode);
    }
    virtual ~SizeHWnd()
    {
        SetWindowPos(_hWnd, NULL, _left, _top, _width, _height, _mode);
    }
};
//--------------------------------------------------------- 
/**
 * @brief Windows窗口句柄类辅助接口
 * @date 2018-08-27 09:37
 */
class HWndHelper
{
protected:
    HWndHelper() {}
public:
    /**
     * @brief 获取指定窗口句柄所在的进程ID
     * @date 2018-08-27 11:16
     */
    static DWORD GetProcessID(HWND hWnd, DWORD* pTid = NULL)
    {
        DWORD pID = 0;
        DWORD tID = GetWindowThreadProcessId(hWnd, &pID);
        if(pTid != NULL)
        {
            (*pTid) = tID;
        }
        return pID;
    }
    /**
     * @brief 根据进程ID获取主窗口句柄
     * @date 2018-08-27 09:39
     */
    static HWND GetProcessHwnd(DWORD processID)
    {
        HWND hWnd = GetTopWindow(NULL);
        while(hWnd)
        {
            DWORD pId = 0;
            DWORD tId = GetWindowThreadProcessId(hWnd, &pId);

            if(tId != 0)
            {
                if(pId == processID)
                {
                    break;
                }
            }
            hWnd = ::GetNextWindow(hWnd, GW_HWNDNEXT);
        }
        return hWnd;
    }
    /**
     * @brief 捕获BMP图像数据到缓冲区中
     * @date 2018-09-07 09:41
     */
    static BOOL CaptureBmpToBuffer(HDC hDC, int left, int top, int width, int height, 
        ByteBuilder& buff, BITMAP* pBmp = NULL, double zoomSize = 0)
    {
        int nWidth = width;
        int nHeight = height;
        bool isZoom = (zoomSize != 0);
        if(isZoom)
        {
            nWidth = static_cast<int>(width * zoomSize);
            nHeight = static_cast<int>(height * zoomSize);
        }
        HDC hMemDC = CreateCompatibleDC(hDC);
        HBITMAP hBmp = CreateCompatibleBitmap(hDC, nWidth, nHeight);
        HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, hBmp);
        BOOL bOK = FALSE;
        if(isZoom)
        {
            bOK = StretchBlt(hMemDC, 0, 0, nWidth, nHeight, hDC, left, top, width, height, SRCCOPY);
        }
        else
        {
            bOK = BitBlt(hMemDC, 0, 0, width, height, hDC, left, top, SRCCOPY);
        }
        if(!bOK)
        {
            SelectObject(hMemDC, hOldBmp);
            DeleteObject(hBmp);
            DeleteDC(hMemDC);
            return FALSE;
        }
        BITMAP bmp = { 0 };
        GetObject(hBmp, sizeof(BITMAP), &bmp);
        if(pBmp != NULL) memcpy(pBmp, &bmp, sizeof(BITMAP));

        BITMAPINFOHEADER bi = { 0 };
        bi.biSize = sizeof(BITMAPINFOHEADER);
        bi.biCompression = BI_RGB;
        bi.biWidth = bmp.bmWidth;
        bi.biHeight = bmp.bmHeight;
        bi.biPlanes = bmp.bmPlanes;
        bi.biBitCount = bmp.bmBitsPixel;
        DWORD dwSize = bmp.bmWidthBytes * bmp.bmHeight;
        bi.biSizeImage = dwSize;

        buff.Clear();
        buff.Append(static_cast<byte>(0x00), dwSize);
        GetDIBits(hMemDC, hBmp, 0, bmp.bmHeight,
            ctype_cast(BYTE*)buff.GetBuffer(),
            ctype_cast(BITMAPINFO*)&bi, DIB_RGB_COLORS);
        SelectObject(hMemDC, hOldBmp);
        DeleteObject(hBmp);
        DeleteDC(hMemDC);
        return TRUE;
    }
    /**
     * @brief 捕获BMP图像数据到缓冲区中
     * @date 2018-09-07 09:53
     */
    static BOOL CaptureBmpToBuffer(HDC hDC, const RECT& rect, ByteBuilder& buff, BITMAP* pBmp = NULL)
    {
        int width = rect.right - rect.left;
        int height = rect.bottom - rect.top;
        return CaptureBmpToBuffer(hDC, rect.left, rect.top, width, height, buff, pBmp);
    }
    /**
     * @brief 将图片数据保存为文件
     * @date 2018-09-20 13:42
     */
    static BOOL SaveToBmp(const ByteArray& buff, BITMAP& bmp, const char* path)
    {
        BITMAPINFOHEADER bi = { 0 };
        bi.biSize = sizeof(BITMAPINFOHEADER);
        bi.biCompression = BI_RGB;
        bi.biWidth = bmp.bmWidth;
        bi.biHeight = bmp.bmHeight;
        bi.biPlanes = bmp.bmPlanes;
        bi.biBitCount = bmp.bmBitsPixel;
        DWORD dwSize = bmp.bmWidthBytes * bmp.bmHeight;
        bi.biSizeImage = dwSize;

        BITMAPFILEHEADER bf = { 0 };
        bf.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
        bf.bfSize = bf.bfOffBits + dwSize;
        bf.bfType = 0x4D42;

        ofstream fout;
        fout.open(path, ios::binary | ios::out);
        if(fout.fail()) return FALSE;

        fout.write(ctype_cast(char*)(&bf), sizeof(BITMAPFILEHEADER));
        fout.write(ctype_cast(char*)(&bi), sizeof(BITMAPINFOHEADER));
        fout.write(buff.GetString(), dwSize);
        fout.close();
        return TRUE;
    }
    /**
     * @brief 获取指定坐标的RGB值
     * @date 2018-09-20 10:48
     */
    static COLORREF GetColor(const ByteArray& buff, int x, int y, BITMAP& bmp)
    {
        int offset = x * (bmp.bmBitsPixel / 8) + (bmp.bmHeight - 1 - y) * bmp.bmWidthBytes;
        return RGB(buff[offset + 2], buff[offset + 1], buff[offset]);
    }
    /**
     * @brief 设置指定坐标的RGB值
     * @date 2018-09-20 11:06
     */
    static COLORREF SetColor(ByteBuilder& buff, int x, int y, BITMAP& bmp, COLORREF color)
    {
        int offset = x * (bmp.bmBitsPixel / 8) + (bmp.bmHeight - 1 - y) * bmp.bmWidthBytes;
        COLORREF oldColor = RGB(buff[offset + 2], buff[offset + 1], buff[offset]);
        buff[offset + 2] = GetRValue(color);
        buff[offset + 1] = GetGValue(color);
        buff[offset] = GetBValue(color);
        return oldColor;
    }
    /**
     * @brief 截取HDC指定大小的区域到图片中
     * @date 2018-08-27 09:41
     */
    static BOOL ScreenShotToBmp(HDC hDC, int left, int top, int width, int height, const char* path, double zoomSize = 0)
    {
        ByteBuilder buff;
        BITMAP bmp = { 0 };
        if(!CaptureBmpToBuffer(hDC, left, top, width, height, buff, &bmp, zoomSize))
            return FALSE;
        return SaveToBmp(buff, bmp, path);
    }
    /**
     * @brief 截取窗口指定大小的区域到图片中
     * @date 2018-09-18 18:27
     */
    static BOOL ScreenShotToBmp(HWND hWnd, int left, int top, int width, int height, const char* path)
    {
        HDC hSrcDC = GetWindowDC(hWnd);
        BOOL bOK = ScreenShotToBmp(hSrcDC, left, top, width, height, path);
        ReleaseDC(hWnd, hSrcDC);
        return bOK;
    }
    /**
     * @brief 从桌面截取整个窗口图片(用于基于DirectX的程序)
     * @date 2018-08-27 09:41
     */
    static BOOL ScreenShotToBmp(HWND hWnd, const char* path)
    {
        RECT rect;
        GetWindowRect(hWnd, &rect);

        int width = rect.right - rect.left;
        int height = rect.bottom - rect.top;
        HWND hDesktop = GetDesktopWindow();

        return ScreenShotToBmp(hDesktop, rect.left, rect.top, width, height, path);
    }
    /**
     * @brief 截取整个窗口的图片
     * @date 2018-08-27 09:41
     */
    static BOOL SaveHwndToBmp(HWND hWnd, const char* path)
    {
        RECT rect;
        GetWindowRect(hWnd, &rect);

        int width = rect.right - rect.left;
        int height = rect.bottom - rect.top;

        return ScreenShotToBmp(hWnd, 0, 0, width, height, path);
    }
    /**
     * @brief 返回窗口句柄是否为主程序窗口句柄
     * @date 2018-08-27 09:43
     */
    static inline bool IsMainHwnd(HWND hWnd)
    {
        return IsWindow(hWnd) &&
            IsWindowVisible(hWnd) &&
            ((GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) != WS_EX_TOOLWINDOW);
    }
};
//--------------------------------------------------------- 
} // namespace ability 
} // namespace extension 
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_HWNDHELPER_H_
//========================================================= 